package cn.shadow.aop;

import cn.shadow.aop.advisor.Advisor;
import cn.shadow.aop.advisor.PointcutAdvisor;
import cn.shadow.aop.pointcut.Pointcut;
import cn.shadow.beans.BeanFactory;
import org.apache.commons.collections4.CollectionUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * Aop代理工具
 * @author 冯啸迪
 */
public class AopProxyUtil {

    /**
     * 获取切面通知实例，然后生成执行链
     * @param target 代理对象
     * @param method 代理对象执行的方法
     * @param args 代理对象执行方法的参数
     * @param matchAdvisor 代理对象的所有通知切面
     * @param proxy 代理
     * @param beanFactory 工厂
     * @return 执行结果
     * @throws Throwable 抛出异常
     */
    public static Object applyAdvice(Object target, Method method, Object[] args, List<Advisor> matchAdvisor, Object proxy, BeanFactory beanFactory) throws Throwable {
        List<Object> adviceInstanceList = getShouldApplyAdvices(target.getClass(), method, matchAdvisor, beanFactory);
        if (CollectionUtils.isEmpty(adviceInstanceList)){
            return method.invoke(target, args);
        }
        AopAdviceChainInvocation chain = new AopAdviceChainInvocation(proxy, target, method, args, adviceInstanceList);
        return chain.invoke();
    }

    /**
     * 之前的判断是否加载，做的是针对类的，现在这哦应该是针对方法的，并且获取Advice实例
     * @param beanClass Bean的类
     * @param method 当前执行的方法
     * @param advisorList Bean类下的所有需要的通知
     * @param beanFactory bean工厂
     * @return 返回当前方法
     */
    private static List<Object> getShouldApplyAdvices(Class<?> beanClass, Method method, List<Advisor> advisorList, BeanFactory beanFactory) throws Throwable {
        List<Object> advisors = new ArrayList<>();
        for (Advisor advisor : advisorList){
            if (advisor instanceof PointcutAdvisor){
                Pointcut pc = ((PointcutAdvisor) advisor).getPointcut();
                if (pc.matchMethod(method, beanClass)){
                    advisors.add(beanFactory.getBean(advisor.getAdvisorBeanName()));
                }
            }
        }
        return advisors;
    }

}
